package com.google.code.gronono.gps.ui.panels.pages;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.io.File;
import java.net.URL;

import javax.swing.GroupLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.GroupLayout.Alignment;
import javax.swing.LayoutStyle.ComponentPlacement;
import javax.swing.event.DocumentEvent;

import org.apache.commons.lang.StringUtils;

import com.google.code.gronono.commons.io.file.FileUtils;
import com.google.code.gronono.commons.swing.listeners.ModifyListener;
import com.google.code.gronono.gps.model.Configuration;
import com.google.code.gronono.gps.model.enums.ProcessAction;
import com.google.code.gronono.gps.ui.GPSFrame;
import com.google.code.gronono.gps.ui.controller.GPSController;
import com.google.code.gronono.gps.ui.controller.actions.BrowseAction;

import fr.gronono.gbundle.Bundle;

/**
 * Page affichant les options principales.
 *
 * @author Arnaud BRUNET
 */
@Bundle("com.google.code.gronono.gps.gui")
public class OptionsPanel extends AbstractPagePanel {
	/** serialVersionUID. */
	private static final long serialVersionUID = -7530395900224664096L;

	/** Titre de la page. */
	@Bundle("options.page.title")
	private static String PAGE_TITLE;
	/** Description de la page. */
	@Bundle("options.page.description")
	private static String PAGE_DESCRIPTION;

	/** Label du dossier source. */
	@Bundle("options.page.src.dir.lbl")
	private static String SRC_DIR_LBL;
	/** Tooltip du dossier source. */
	@Bundle("options.page.src.dir.txt.tooltip")
	private static String SRC_DIR_TXT_TOOLTIP;
	/** Largeur min de la zone de saisie du dossier source. */
	@Bundle("options.page.src.dir.txt.min.width")
	private static int SRC_DIR_TXT_MIN_WIDTH;
	/** Titre du message d'erreur sur la validation du dossier. */
	@Bundle("options.page.src.dir.err.title")
	private static String SRC_DIR_ERR_TITLE;
	/** Message d'erreur sur la validation du dossier. */
	@Bundle("options.page.src.dir.err.msg")
	private static String SRC_DIR_ERR_MSG;

	/** Label du dossier destination. */
	@Bundle("options.page.dst.dir.lbl")
	private static String DST_DIR_LBL;
	/** Tooltip du dossier destination. */
	@Bundle("options.page.dst.dir.txt.tooltip")
	private static String DST_DIR_TXT_TOOLTIP;
	/** Titre du message d'erreur sur la validation du dossier. */
	@Bundle("options.page.dst.dir.err.title")
	private static String DST_DIR_ERR_TITLE;
	/** Message d'erreur sur la validation du dossier. */
	@Bundle("options.page.dst.dir.err.msg")
	private static String DST_DIR_ERR_MSG;
	/** Message d'erreur sur la validation du dossier. */
	@Bundle("options.page.dst.dir.err.msg.notfound")
	private static String DST_DIR_ERR_MSG_NOTFOUND;

	/** Label du bouton d'affichage des options avancées. */
	@Bundle("options.page.advanced.options.show.lbl")
	private static String ADVANCED_OPTIONS_SHOW_LBL;
	/** Label du bouton d'affichage des options avancées. */
	@Bundle("options.page.advanced.options.hide.lbl")
	private static String ADVANCED_OPTIONS_HIDE_LBL;
	/** Tooltip du bouton d'affichage des options avancées. */
	@Bundle("options.page.advanced.options.tooltip")
	private static String ADVANCED_OPTIONS_TOOLTIP;
	/** Icône du bouton d'affichage des options avancées. */
	@Bundle("options.page.advanced.options.icon.display")
	private static String ADVANCED_OPTIONS_ICON_DISPLAY;
	/** Icône du bouton d'affichage des options avancées. */
	@Bundle("options.page.advanced.options.icon.collapse")
	private static String ADVANCED_OPTIONS_ICON_COLLAPSE;

	/**  Label du dossier source. */
	private JLabel srcDirLbl;
	/** Champ de saisie du dossier source. */
	private JTextField srcDirTxt;
	/** Bouton de parcours du dossier source. */
	private JButton srcDirBrowseBtn;

	/** Label du dossier de destination. */
	private JLabel dstDirLbl;
	/** Champ de saisie du dossier de destination. */
	private JTextField dstDirTxt;
	/** Bouton de parcours du dossier de destination. */
	private JButton dstDirBrowseBtn;

	/** Bouton d'affichage des options avancées. */
	private JButton advancedOptionsBtn;
	/** Le séparateur entre les options de base et les options avancées. */
	private JSeparator separator;

	/** Panel des options avancées. */
	private AdvancedOptionsPanel advancedOptionsPanel;

	/** La configuration de l'application. */
	private final Configuration configuration;
	/** La frame principale de l'application. */
	private final GPSFrame gpsFrame;

	/**
	 * Construit la page.
	 * @param controller Le contrôleur de l'application.
	 */
	public OptionsPanel(final GPSController controller) {
		super(controller, PAGE_TITLE, PAGE_DESCRIPTION);
		configuration = controller.getConfiguration();
		gpsFrame = controller.getGPSFrame();

		initComponents();
		placeComponents();

		initValues();
	}

	/**
	 * Initialise les différents composants.
	 */
	private void initComponents() {
		srcDirLbl = new JLabel(SRC_DIR_LBL);
		srcDirTxt = new JTextField();
		srcDirTxt.setMinimumSize(new Dimension(SRC_DIR_TXT_MIN_WIDTH, 5)); //TODO Voir s'il n'y aurait pas plus élégant (label en html => comportement différent sur la largeur min de cette zone)
		srcDirTxt.setToolTipText(SRC_DIR_TXT_TOOLTIP);
		srcDirTxt.addFocusListener(new FocusAdapter() {
			@Override
			public void focusLost(final FocusEvent arg0) {
				final String srcDir = StringUtils.trimToEmpty(srcDirTxt.getText());
				configuration.setSrcDir(srcDir);
				// Validation du dossier
				final File dir = new File(srcDir);
				try {
					FileUtils.checkCanReadDir(dir);
					if (configuration.getProcessAction() == ProcessAction.MOVE) {
						FileUtils.checkCanWriteDir(dir, false);
					}
				} catch (final Exception e) {
					// Repositionne le curseur sur le prochain dossier de plus haut niveau existant
					final File highestExistingDir = FileUtils.getHighestExistingParentFolder(dir);
					if (highestExistingDir != null) srcDirTxt.setText(highestExistingDir.getAbsolutePath());
					final StringBuilder msg = new StringBuilder(SRC_DIR_ERR_MSG).append("\n").append(e.getMessage());
					JOptionPane.showMessageDialog(gpsFrame, msg, SRC_DIR_ERR_TITLE, JOptionPane.ERROR_MESSAGE);
				}
			}
		});
		srcDirTxt.getDocument().addDocumentListener(new ModifyListener() {
			@Override
			public void modify(final DocumentEvent event) {
				configuration.setSrcDir(StringUtils.trimToEmpty(srcDirTxt.getText()));
			}
		});
		srcDirBrowseBtn = new JButton(controller.getBrowseAction());
		srcDirBrowseBtn.putClientProperty(BrowseAction.FIELD_KEY, srcDirTxt);

		dstDirLbl = new JLabel(DST_DIR_LBL);
		dstDirTxt = new JTextField();
		dstDirTxt.setToolTipText(DST_DIR_TXT_TOOLTIP);
		dstDirTxt.addFocusListener(new FocusAdapter() {
			@Override
			public void focusLost(final FocusEvent arg0) {
				final String dstDir = StringUtils.trimToEmpty(dstDirTxt.getText());
				configuration.setDstDir(dstDir);
				// Validation du dossier
				final File dir = new File(dstDir);
				if (!dir.exists()) {
					JOptionPane.showMessageDialog(gpsFrame, DST_DIR_ERR_MSG_NOTFOUND, DST_DIR_ERR_TITLE, JOptionPane.WARNING_MESSAGE);
				}
				else {
					try {
						FileUtils.checkCanWriteDir(dir, false);
					} catch (final Exception e) {
						// Repositionne le curseur sur le prochain dossier de plus haut niveau existant
						final File highestExistingDir = FileUtils.getHighestExistingParentFolder(dir);
						if (highestExistingDir != null) dstDirTxt.setText(highestExistingDir.getAbsolutePath());
						final StringBuilder msg = new StringBuilder(DST_DIR_ERR_MSG).append("\n").append(e.getMessage());
						JOptionPane.showMessageDialog(gpsFrame, msg, DST_DIR_ERR_TITLE, JOptionPane.WARNING_MESSAGE);
					}
				}
			}
		});
		dstDirTxt.getDocument().addDocumentListener(new ModifyListener() {
			@Override
			public void modify(final DocumentEvent event) {
				final String dstDir = StringUtils.trimToEmpty(dstDirTxt.getText());
				configuration.setDstDir(dstDir);
				// Rmq: pour l'instant on force rejectDir=dstDir
				advancedOptionsPanel.getRejectDirTxt().setText(dstDir);
			}
		});
		dstDirBrowseBtn = new JButton(controller.getBrowseAction());
		dstDirBrowseBtn.putClientProperty(BrowseAction.FIELD_KEY, dstDirTxt);

		advancedOptionsBtn = new JButton(ADVANCED_OPTIONS_SHOW_LBL);
		advancedOptionsBtn.setToolTipText(ADVANCED_OPTIONS_TOOLTIP);
		final URL displayAdvancedOptionsIconUrl = this.getClass().getClassLoader().getResource(ADVANCED_OPTIONS_ICON_DISPLAY);
		final ImageIcon displayAdvancedOptionsIcon = new ImageIcon(displayAdvancedOptionsIconUrl);
		final URL collapseAdvancedOptionsIconUrl = this.getClass().getClassLoader().getResource(ADVANCED_OPTIONS_ICON_COLLAPSE);
		final ImageIcon collapseAdvancedOptionsIcon = new ImageIcon(collapseAdvancedOptionsIconUrl);
		advancedOptionsBtn.setIcon(displayAdvancedOptionsIcon);
		advancedOptionsBtn.addActionListener(new ActionListener() {
			public void actionPerformed(final ActionEvent e) {
				final boolean isAdvanceOptionsPanelVisible = advancedOptionsPanel.isVisible();
				advancedOptionsPanel.setVisible(!isAdvanceOptionsPanelVisible);
				if (isAdvanceOptionsPanelVisible) {
					advancedOptionsBtn.setText(ADVANCED_OPTIONS_SHOW_LBL);
					advancedOptionsBtn.setIcon(displayAdvancedOptionsIcon);
				} else {
					advancedOptionsBtn.setText(ADVANCED_OPTIONS_HIDE_LBL);
					advancedOptionsBtn.setIcon(collapseAdvancedOptionsIcon);
				}
			}
		});

		separator = new JSeparator();

		advancedOptionsPanel = new AdvancedOptionsPanel(controller);
		advancedOptionsPanel.setVisible(false);
	}

	/**
	 * Place les composants dans le panneau.
	 */
	private void placeComponents() {
		final GroupLayout layout = new GroupLayout(this);
		setLayout(layout);

		// Horizontalement
		layout.setHorizontalGroup(
				layout.createParallelGroup()
				.addGroup(layout.createSequentialGroup()
						.addGroup(layout.createParallelGroup()
								.addComponent(srcDirLbl)
								.addComponent(dstDirLbl))
								.addGroup(layout.createParallelGroup()
										.addGroup(layout.createSequentialGroup()
												.addComponent(srcDirTxt)
												.addPreferredGap(ComponentPlacement.RELATED)
												.addComponent(srcDirBrowseBtn))
												.addGroup(layout.createSequentialGroup()
														.addComponent(dstDirTxt)
														.addPreferredGap(ComponentPlacement.RELATED)
														.addComponent(dstDirBrowseBtn))))
														.addGroup(layout.createParallelGroup()
																.addGroup(layout.createSequentialGroup()
																		.addComponent(advancedOptionsBtn)
																		.addPreferredGap(ComponentPlacement.RELATED)
																		.addComponent(separator)))
																		.addComponent(advancedOptionsPanel));
		// Verticalement
		layout.setVerticalGroup(
				layout.createSequentialGroup()
				.addContainerGap()
				.addGroup(layout.createParallelGroup(Alignment.BASELINE)
						.addComponent(srcDirLbl)
						.addComponent(srcDirTxt)
						.addComponent(srcDirBrowseBtn))
						.addPreferredGap(ComponentPlacement.RELATED)
						.addGroup(layout.createParallelGroup(Alignment.BASELINE)
								.addComponent(dstDirLbl)
								.addComponent(dstDirTxt)
								.addComponent(dstDirBrowseBtn))
								.addGap(20)
								.addGroup(layout.createParallelGroup()
										.addComponent(separator)
										.addComponent(advancedOptionsBtn))
										.addPreferredGap(ComponentPlacement.RELATED)
										.addComponent(advancedOptionsPanel)
										// Note : les gaps ci-dessous ont pour objet de 'résoudre' tant bien que mal
										// le pb du décalage vers le bas des options avancées lorsqu'on maximise la fenêtre
										//TODO Trouver une solution plus élégante...
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE)
										.addGap(0, 0, Integer.MAX_VALUE).addGap(0, 0, Integer.MAX_VALUE));
	}

	/**
	 * Initialise les valeurs en fonction de la configuration.
	 */
	public void initValues() {
		srcDirTxt.setText(configuration.getSrcDir().getAbsolutePath());
		dstDirTxt.setText(configuration.getDstDir().getAbsolutePath());
		advancedOptionsPanel.initValues();
	}
}
